package opencm;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.joda.time.LocalDate;

public final class Perfil implements Serializable {

	private String nome;
	private String cpf;
	private String foneResidencial;
	private String foneComercial = null;
	private String foneCelular = null;
	private String email = null;
	private Endereco endereco = null;
	private LocalDate dataNascimento = null;
	private TipoContato contatoPreferencial = null;

	private static final int IDADE_MAXIMA = 150;
	private static final int TAMANHO_MAXIMO_NOME = 130;
	private static final String FORMATO_TELEFONE = "^\\(\\d{2}\\)\\d{4}-\\d{4}$";
	private static final String FORMATO_EMAIL = "^[\\w-]+(\\.[\\w-]+)*@([\\w-]+\\.)+[a-zA-Z]{2,7}$";
	private static final String FORMATO_CPF = "^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$";

	private static final long serialVersionUID = 1L;

	public enum TipoContato {
		FONE_RESIDENCIAL("Telefone Residencial"), FONE_COMERCIAL("Telefone Comercial"), FONE_CELULAR(
				"Celular");

		private final String descricao;

		TipoContato(String descricao) {
			this.descricao = descricao;
		}

		@Override
		public String toString() {
			return this.descricao;
		}

		public static List<TipoContato> toList() {
			return Arrays.asList(values());
		}
	}

	/**
	 * Construtor unico de perfil.
	 * @param nome Nome do perfil.
	 * @param cpf CPF do perfil.
	 * @param foneResidencial Telefone residencial do perfil.
	 */
	public Perfil(String nome, String cpf, String foneResidencial) {
		setNome(nome);
		setCpf(cpf);
		setFoneResidencial(foneResidencial);
	}
	
	@Override
	public boolean equals(Object obj) {
		return EqualsBuilder.reflectionEquals(this, obj);
	}
	
	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this);
	}
	
	@Override
	public String toString() {
		return getNome();
	}

	/**
	 * Retorna o o contato preferencial do perfil.
	 * @return Contato preferencial.
	 */
	public String getValorContatoPreferencial() {
		switch (getContatoPreferencial()) {
		case FONE_RESIDENCIAL:
			return getFoneResidencial();
		case FONE_COMERCIAL:
			return getFoneComercial();
		case FONE_CELULAR:
			return getFoneCelular();
		default:
			return getFoneResidencial();
		}
	}

	/**
	 * Muda o nome do perfil.
	 * @param nome Novo nome.
	 */
	public void setNome(String nome) {
		Validate.notBlank(nome, "Nome não pode estar em branco.");
		Validate.isTrue(nome.length() < TAMANHO_MAXIMO_NOME, "Nome muito longo.");
		this.nome = nome;
	}

	/**
	 * Muda o CPF do perfil.
	 * @param cpf Novo cpf.
	 */
	public void setCpf(String cpf) {
		Validate.notBlank(cpf, "CPF não pode estar em branco.");
		Validate.matchesPattern(cpf, FORMATO_CPF, "CPF inválido.");
		this.cpf = cpf;
	}

	/**
	 * Muda o telefone residencial do perfil.
	 * @param foneResidencial Novo telefone residencial.
	 */
	public void setFoneResidencial(String foneResidencial) {
		Validate.notBlank(foneResidencial, "Fone Residêncial não pode estar em branco.");
		Validate.matchesPattern(foneResidencial, FORMATO_TELEFONE, "Fone Residêncial inválido.");
		this.foneResidencial = foneResidencial;
	}

	/**
	 * Muda o endereco do perfil.
	 * @param endereco Novo endereco.
	 */
	public void setEndereco(Endereco endereco) {
		this.endereco = endereco;
	}

	/**
	 * Muda o telefone comercial do perfil.
	 * @param foneComercial Novo telefone comercial.
	 */
	public void setFoneComercial(String foneComercial) {
		if (foneComercial != null) {
			Validate.matchesPattern(foneComercial, FORMATO_TELEFONE, "Fone Comercial inválido.");
		}
		this.foneComercial = foneComercial;
	}

	/**
	 * Muda o telefone celular do perfil.
	 * @param foneCelular Novo telefone celular.
	 */
	public void setFoneCelular(String foneCelular) {
		if (foneCelular != null) {
			Validate.matchesPattern(foneCelular, FORMATO_TELEFONE, "Fone Celular inválido.");
		}
		this.foneCelular = foneCelular;
	}

	/**
	 * Muda a data de nascimento do perfil.
	 * @param dataNascimento Nova data de nascimento.
	 */
	public void setDataNascimento(LocalDate dataNascimento) {
		if (dataNascimento != null) {
			Validate.isTrue(dataNascimento.isAfter(new LocalDate().minusYears(IDADE_MAXIMA)),
					"Data de Nascimento muito antiga.");
			Validate.isTrue(dataNascimento.toDateTimeAtStartOfDay().isBeforeNow(),
					"Data de nascimento invalida.");
		}
		this.dataNascimento = dataNascimento;
	}

	/**
	 * Muda o e-mail do perfil.
	 * @param email Novo e-mail.
	 */
	public void setEmail(String email) {
		if (email != null) {
			Validate.matchesPattern(email, FORMATO_EMAIL, "Email inválido.");
		}
		this.email = email;
	}

	/**
	 * Muda o tipo de contato preferencial do perfil.
	 * @param Novo tipo de contato prefencial.
	 */
	public void setContatoPreferencial(TipoContato contatoPreferencial) {
		this.contatoPreferencial = contatoPreferencial;
	}

	/**
	 * Retorna o nome do perfil.
	 * @return Nome
	 */
	public String getNome() {
		return nome;
	}

	/**
	 * Retorna o CPF do perfil
	 * @return CPF
	 */
	public String getCpf() {
		return cpf;
	}

	/**
	 * Retorna o endereco do perfil.
	 * @return Endereco
	 */
	public Endereco getEndereco() {
		return endereco;
	}

	/**
	 * Retorna o telefone residencial do perfil.
	 * @return Telefone residencial
	 */
	public String getFoneResidencial() {
		return foneResidencial;
	}

	/**
	 * Retorna o telefone comercial do perfil.
	 * @return Telefone comercial
	 */
	public String getFoneComercial() {
		return foneComercial;
	}

	/**
	 * Retorna o telefone celular do perfil.
	 * @return Telefone celular
	 */
	public String getFoneCelular() {
		return foneCelular;
	}

	/**
	 * Rertorna a data de nascimento do perfil.
	 * @return Data de nascimento
	 */
	public LocalDate getDataNascimento() {
		return dataNascimento;
	}

	/**
	 * Retorna o e-mail do perfil.
	 * @return E-mail
	 */
	public String getEmail() {
		return email;
	}

	/**
	 * Retorna o tipo de contato prefencial do perfil.
	 * @returns Tipo de contato preferencial.
	 */
	public TipoContato getContatoPreferencial() {
		return contatoPreferencial;
	}

}